/*******************************************************************************
 * Copyright 2012 Erik Gast
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package easyobserving;

import java.lang.reflect.Field;

import easyobserving.annotation.Observable;


public class ObserverHelper {
	
	/**
	 * Searches if instance is an observable or has a observable member with the name "name".
	 * If a observable is found, observers is added to this observable.
	 * @param <T> the observer type
	 * @param instance the observable instance or instance containing a observable member with name "name"
	 * @param observableName the name of the observable
	 * @param observer the observer to add
	 * @return true is the observer is added to a observable, else false is returned
	 */
	public static <T> boolean addToObservable(Object instance, String observableName,  T observer){
		ObserverRegistrable<T> handler = findObservable(instance, observableName);
		if(handler != null){
			handler.addObserver(observer);
			return true;
		}
		return false;
	}
	
	/**
	 * Searches if instance is an observable or has a observable member with the name "name".
	 * If a observable is found, observers is removed from this observable.
	 * @param <T> the observer type
	 * @param instance the observable instance or instance containing a observable member with name "name"
	 * @param observableName the name of the observable
	 * @param observer the observer to remove
	 * @return true is the observer is removed from a observable, else false is returned
	 */
	public static <T> boolean removeFromObservable(Object instance, String observableName,  T listener){
		ObserverRegistrable<T> handler = findObservable(instance, observableName);
		if(handler != null){
			handler.removeObserver(listener);
			return true;
		}
		return false;
	}
	
	public static boolean hasObservable(Object instance, String observableName){
		return findObservable(instance, observableName) != null;
	}
	
	@SuppressWarnings("unchecked")
	private static <T> ObserverRegistrable<T> findObservable(Object instance, String observableName){
		Class<?> cl = instance.getClass();
		Observable obsClassAnnotation = cl.getAnnotation(Observable.class);
		if(obsClassAnnotation != null && instance instanceof ObserverRegistrable<?> && obsClassAnnotation.name().equals(observableName)){
			return (ObserverRegistrable<T>)instance;
		}else{
			Field[] fields = cl.getDeclaredFields();
			for(Field field: fields){
				Observable annotation = field.getAnnotation(Observable.class);
				if(annotation != null && annotation.name().equals(observableName)){
					field.setAccessible(true);
					try {
						if(ObserverHandler.class.isAssignableFrom(field.getType())){
							return (ObserverRegistrable<T>) field.get(instance);
						}else{
							throw new RuntimeException("\""+observableName + "\" in "+instance.getClass()+" must be of type \""+ObserverHandler.class.getName()+"\"");
						}
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}finally{
						field.setAccessible(false);
					}
				}
			}
		}
		return null;
	}
}
